local skynet = require "skynet"
local table_insert = table.insert
local table_remove = table.remove
local math_rand = math.random
local msghelper = require "tablehelper"
local timetool = require "timetool"
local tabletool = require "tabletool"
local timer = require "timer"
local filelog = require "filelog"
local json = require "cjson"
json.encode_sparse_array(true,1,1)
require "gang28enum"
local gang28 = require "gang28"

local tablelogic
local seatlogic
local server
local table_data

local GameLogic = {}

function GameLogic.init(tablebase)
	server = tablebase
	table_data = server.table_data
	tablelogic = tablebase.logic.tablelogic
	seatlogic  = tablebase.logic.seatlogic
	return true
end

function GameLogic.gamestart()
	GameLogic.onegamestart_inittable()
	if table_data.conf.match_type and table_data.the_round == 1 then
		skynet.fork(function()
			skynet.sleep(3 * 100)
			if table_data.state ~= ETableState.TABLE_STATE_GAME_START then return end
			table_data.the_round = table_data.the_round + 1
			GameLogic.riffleandpostcards()
		end)
		return
	end
	table_data.the_round = table_data.the_round + 1
	-- 洗牌
	GameLogic.riffleandpostcards()
end

function GameLogic.onegamestart_inittable()
	table_data.state = ETableState.TABLE_STATE_GAME_START
	table_data.initcards = {}		-- 牌池
	table_data.gamerecords = {}
	table_data.tmpdiamond = {}
	table_data.horseracelamps = {}
	table_data.recycled = nil
	table_data.bankercallers = {{}, {}}
	table_data.banker = nil
	table_data.tip = nil
	table_data.playingnum = 0
	table_data.calledcount = 0
	table_data.betedcount = 0
	table_data.showcount = 0
	if table_data.timer_id and table_data.timer_id > 0 then
		timer.cleartimer(table_data.timer_id)
		table_data.timer_id = -1
		table_data.waittotime = nil
	end
	table_data.cound = table_data.round + 1

	for _, seat in ipairs(table_data.seats) do
		if seat.state ~= ESeatState.SEAT_STATE_NO_PLAYER then
			seatlogic.onegamestart_initseat(seat)
			table_data.playingnum = table_data.playingnum + 1
		end
	end
end

function GameLogic.riffleandpostcards()
	-- 生成一副牌 洗牌
	gang28.shuffle(table_data)
	table_data.gamerecords.initcards = json.encode(table_data.initcards)

	local rids = {}
	local playerdata = {}
	for _, seat in ipairs(table_data.seats) do
		if seat.state == ESeatState.SEAT_STATE_PLAYING then
			table.insert(rids, seat.rid)
			playerdata[seat.rid] = {
				name = seat.playerinfo.rolename,
				logo = seat.playerinfo.logo,
				sex = seat.playerinfo.sex,
				diamond = seat.diamond,
			}
		end
	end

	local now = timetool.get_time()
	if table_data.conf.room_type ~= ERoomType.ROOM_TYPE_SYSTEM then
		table.sort(rids, function(l, r)
			return l < r
		end)
		local ridsstr = ''
		for _, rid in ipairs(rids) do
			if ridsstr == '' then
				ridsstr = ridsstr .. rid
			else
				ridsstr = ridsstr .. '_' .. rid
			end
		end
		-- init gamerecords
		table_data.gamerecords.start_time = now
		table_data.gamerecords.table_uuid = "gangnode" .. "_"	-- skynet.getenv("nodename") .. "_"
			.. table_data.conf.table_code .. "_"
			.. table_data.conf.idx .. "_" .. table_data.conf.create_time
		table_data.gamerecords.logname = '' .. table_data.gamerecords.table_uuid .. '_'
			.. now .. '_' .. '0' .. '_'
			.. ridsstr .. '_' .. os.date("%Y%m%d_%H%M%S", now) .. '_'
			.. table_data.conf.game_room_type
		table_data.gamerecords.rids = ridsstr
		table_data.gamerecords.conf = json.encode(table_data.conf)
		-- table_data.gamerecords.playerdata = json.encode(playerdata)
		table_data.gamerecords.playerdata = playerdata
	end
	table_data.conf.curr_round_code = string.sub(tostring(now), -4, -1)

	if table_data.conf.room_type == ERoomType.ROOM_TYPE_DIAMOND or table_data.conf.room_type == ERoomType.ROOM_TYPE_COIN then
		tablelogic.caclate_tip(table_data)
		tablelogic.pay_tip(table_data)
		----游戏开始扣除奖池抽水
		-- tablelogic.pay_jackopt(table_data)
		table_data.gamerecords.tip = table_data.tip
	end

	local gamestartntcmsg = {}
	gamestartntcmsg.gameinfo = {}
	msghelper:copy_table_gameinfo(gamestartntcmsg.gameinfo)
	msghelper:sendmsg_to_alltableplayer("gamestartN", gamestartntcmsg)

	-- 给玩家发手牌
	if table_data.conf.game_type == EGameType.GAME_TYPE_SEECARD then
		GameLogic.sendcards(1)
	end

	-- 抢庄阶段
	table_data.state = ETableState.TABLE_STATE_CALL
	skynet.sleep(2 * 100)
	if table_data.state ~= ETableState.TABLE_STATE_CALL then return end
	local delay = table_data.conf.action_timeout
	table_data.timer_id = timer.settimer(delay * 100, "callover")
	table_data.waittotime = timetool.get_time() + delay
	msghelper:sendmsg_to_alltableplayer("callbankerN", {
		calltotime = timetool.get_time() + delay,
	})
end

-- 寻找并设置庄家
function GameLogic.setbanker()
	local banker = nil
	local highercallers = nil
	local times = 0
	for i = #table_data.bankercallers, 1, -1 do
		if #table_data.bankercallers[i] > 0 then
			highercallers = table_data.bankercallers[i]
			times = i
			break
		end
	end
	if highercallers then
		banker = highercallers[math_rand(#highercallers)]
	end
	if not banker then
		highercallers = {}
		for _, seat in ipairs(table_data.seats) do
			if seat.state == ESeatState.SEAT_STATE_PLAYING then
				table_insert(highercallers, seat.index)
			end
		end
		banker = highercallers[math_rand(#highercallers)]
		times = 1
	end
	table_data.banker = banker
	table_data.seats[banker].call = times
	msghelper:sendmsg_to_alltableplayer("bankerN", {
		rid = table_data.seats[banker].rid,
		seat_index = banker,
		value = times,
		highercallers = highercallers,
	})

	-- 闲家下注阶段
	table_data.state = ETableState.TABLE_STATE_BET
	skynet.sleep((table_data.playingnum * 0.25 + 0.25) * 100)
	if table_data.state ~= ETableState.TABLE_STATE_BET then return end
	local delay = table_data.conf.action_timeout
	table_data.timer_id = timer.settimer(delay * 100, "betover")
	table_data.waittotime = timetool.get_time() + delay
	msghelper:sendmsg_to_alltableplayer("playerbetN", {
		bettotime = timetool.get_time() + delay,
	})
end

function GameLogic.sendcards(num)
	-- 给玩家发手牌
	for _, seat in ipairs(table_data.seats) do
		if seat.state ~= ESeatState.SEAT_STATE_NO_PLAYER then
			if seat.state == ESeatState.SEAT_STATE_PLAYING then
				if num == 1 then
					table_insert(seat.cards, table_remove(table_data.initcards, 1))
				else
					table_insert(seat.cards, table_remove(table_data.initcards, 1))
					table_insert(seat.cards, table_remove(table_data.initcards, 1))
				end
				if #seat.cards > 1 then
					gang28.sort(seat.cards)

					local handcards = {}
					for _, seat in ipairs(table_data.seats) do
						if seat.state == ESeatState.SEAT_STATE_PLAYING then
							handcards[seat.rid] = tabletool.deepcopy(seat.cards)
						end
					end
					table_data.gamerecords.handcards = json.encode(handcards)

					--[[保存牌型记录
					local playernum = 0
					local cardtypes = {}
					for _, seat in pairs(table_data.seats) do
						if seat.state == ESeatState.SEAT_STATE_PLAYING then
							local card_type = gang28.cardtype(seat.cards)
							playernum = playernum + 1
							table.insert(cardtypes,card_type)
						end
					end
					tablelogic.save_cardtype_record(table_data.conf.game_type, cardtypes, playernum)
					--]]
				end
			end
			-- msghelper:sendmsg_to_alltableplayer("sendcardsN", {
			-- 	rid = seat.rid,
			-- 	seat_index = seat.index,
			-- 	num = num,
			-- })
			msghelper:sendmsg_to_tableplayer(seat, "cardinfoN", {
				rid = seat.rid,
				seat_index = seat.index,
				cards = seat.cards,
			})
		end
	end
end

function GameLogic.checkcards()
	local playerscards = {}
	for _, seat in ipairs(table_data.seats) do
		if seat.state == ESeatState.SEAT_STATE_PLAYING then
			local data = {
				rid = seat.rid,
				seat_index = seat.index,
				cards = seat.cards,
			}
			table_insert(playerscards, data)
		end
	end
	msghelper:sendmsg_to_alltableplayer("playerscardsN", {player_cards_data = playerscards})

	table_data.timer_id = timer.settimer(0.5 * 100, "gameover")
end

function GameLogic.gameover()
	table_data.state = ETableState.TABLE_STATE_GAME_OVER
	local playerdata = table_data.gamerecords.playerdata
	local bankerseat = table_data.seats[table_data.banker]
	local winseats = {}
	local loseseats = {}
	local winall, loseall = 0, 0
	local balancedata = {}
	local isallrobot = true
	for _, seat in ipairs(table_data.seats) do
		if seat.state == ESeatState.SEAT_STATE_PLAYING and seat.index ~= bankerseat.index then
			local times = bankerseat.call
			local tm = 1
			local iswin = gang28.compare(bankerseat.cards, seat.cards)
			local value = seat.bets > 0 and (seat.bets * table_data.conf.base_score) or table_data.conf.base_score
			-- filelog.warn("=============>> iswin =", iswin, bankerseat.index, seat.index)
			if iswin then
				times = times * gang28.gettimes(bankerseat.cards)
				value = value * (times * tm)
				if value > seat.diamond then
					value = seat.diamond
				end
				table_insert(loseseats, {seat.index, value, seat.rid})
				winall = winall + value
				value = -value
				-- filelog.warn("player lose ==========>", value)
			else
				tm = tm * gang28.gettimes(seat.cards)
				value = value * (times * tm)
				if value > seat.diamond then
					value = seat.diamond
				end
				table_insert(winseats, {seat.index, value, seat.rid})
				loseall = loseall - value
				-- filelog.warn("player win  ==========>", value)
			end
			table_insert(balancedata, {
				rid = seat.rid,
				seat_index = seat.index,
				value = value,
				cards = seat.cards,
			})
			playerdata[seat.rid].bets = seat.bets
			playerdata[seat.rid].call = nil
			playerdata[seat.rid].value = value
			playerdata[seat.rid].isbanker = 0
			playerdata[seat.rid].cards = seat.cards

			if seat.rid > 100 and not seat.isrobot then
				isallrobot = false
			end
		end
	end

	-- filelog.debug("balanceN:", bankerseat.index, loseall + winall, balancedata)
	
	if bankerseat.diamond + (winall + loseall) < 0 then
		local realloseall = 0
		local ratio = - (bankerseat.diamond + winall) / loseall
		for _, v in ipairs(winseats) do
			v[2] = math.floor(v[2] * ratio)
			realloseall = realloseall - v[2]
			for _, bal in ipairs(balancedata) do
				if bal.rid == v[3] then
					bal.value = v[2]
				end
			end
			playerdata[v[3]].value = v[2]
		end
		local offset = bankerseat.diamond + realloseall + winall
		if offset ~= 0 then
			local r = math_rand(#winseats)
			winseats[r][2] = winseats[r][2] + offset
			for _, bal in ipairs(balancedata) do
				if bal.rid == winseats[r][3] then
					bal.value = winseats[r][2]
					break
				end
			end
			playerdata[winseats[r][3]].value = winseats[r][2]
		end
		if offset > 0 then
			loseall = realloseall - offset
		else
			loseall = realloseall + offset
		end
	elseif bankerseat.diamond < winall then
		local realwinall = 0
		local ratio = bankerseat.diamond / winall
		for _, v in ipairs(loseseats) do
			v[2] = math.floor(v[2] * ratio)
			realwinall = realwinall + v[2]
			for _, bal in ipairs(balancedata) do
				if bal.rid == v[3] then
					bal.value = -v[2]
				end
			end
			playerdata[v[3]].value = -v[2]
		end
		winall = realwinall
	end

	table_insert(balancedata, {
		rid = bankerseat.rid,
		seat_index = bankerseat.index,
		value = winall + loseall,
		cards = bankerseat.cards,
	})

	playerdata[bankerseat.rid].bets = nil
	playerdata[bankerseat.rid].call = bankerseat.call
	playerdata[bankerseat.rid].value = winall + loseall
	playerdata[bankerseat.rid].isbanker = 1
	playerdata[bankerseat.rid].cards = bankerseat.cards

	-- filelog.debug("balanceN:", bankerseat.index, balancedata)
	msghelper:sendmsg_to_alltableplayer("balanceN", {balance_data = balancedata})
	tablelogic.changediamond(table_data, bankerseat.index, winall + loseall)
	for _, v in ipairs(winseats) do
		tablelogic.changediamond(table_data, v[1], v[2])
	end
	for _, v in ipairs(loseseats) do
		tablelogic.changediamond(table_data, v[1], -v[2])
	end
	tablelogic.change_all_player_money(table_data)

	table_data.gamerecords.playerdata = json.encode(playerdata)
	tablelogic.save_actions(table_data)
	tablelogic.save_actions(table_data, "end")

	skynet.sleep(3 * 100)
	if table_data.state ~= ETableState.TABLE_STATE_GAME_OVER then return end

	--游戏结束发送开奖跑马灯
    -- for _, msg in pairs(table_data.horseracelamps) do
    --     tablelogic.send_horseracelamp(msg.content)
	-- end
	
	if isallrobot then
		if table_data.conf.match_type then
			if math.random(100) <= 20 then
				table_data.delete_table_timer_id = timer.settimer(table_data.conf.retain_time*100, "delete_table")
				return
			end
		else
			table_data.delete_table_timer_id = timer.settimer(table_data.conf.retain_time*100, "delete_table")
			return
		end
	end

	for _, seat in ipairs(table_data.seats) do
		if seat.state ~= ESeatState.SEAT_STATE_NO_PLAYER then
			-------------------------- robot --------------------------
			if seat.isrobot then
				if (seat.playgamecount or 0) > 5 then
					if math.random(100) <= (seat.playgamecount - 5) * 10 then
						tablelogic.passive_standuptable(table_data, seat)
					end
				end
			end

			seat.is_ready = EBOOL.FALSE
			seat.state = ESeatState.SEAT_STATE_WAIT_READY
			seat.cards = {}
			seat.showcard = nil
			local outoftimermsg = {
				rid = seat.rid,
				seat_index = seat.index,
			}
			seat.ready_timer_id = timer.settimer(table_data.conf.ready_timeout*100, "doready", outoftimermsg)
			seat.ready_to_time = timetool.get_time() + table_data.conf.ready_timeout
			local doreadyntcmsg = {
				rid = seat.rid,
				seat_index = seat.index,
				ready_to_time = seat.ready_to_time,
			}
			msghelper:sendmsg_to_alltableplayer("doreadyN", doreadyntcmsg)
		end
	end
	table_data.state = ETableState.TABLE_STATE_WAIT_ALL_READY
	table_data.gaming = false
	table_data.playingnum = 0
	table_data.conf.curr_round_code = nil
end

return GameLogic